home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Special 16 / AMIGAplus Sonderheft 16 (1998)(ICP)(DE)[!].iso / pd / anwendungen / ispell-3.1.18src / parse.y < prev    next >
Text File  |  1994-11-21  |  50KB  |  2,008 lines

  1. %{
  2. #ifndef lint
  3. static char Rcs_Id[] =
  4.     "$Id: parse.y,v 1.52 1994/11/21 07:03:03 geoff Exp $";
  5. #endif
  6.  
  7. /*
  8.  * Copyright 1992, 1993, Geoff Kuenning, Granada Hills, CA
  9.  * All rights reserved.
  10.  *
  11.  * Redistribution and use in source and binary forms, with or without
  12.  * modification, are permitted provided that the following conditions
  13.  * are met:
  14.  *
  15.  * 1. Redistributions of source code must retain the above copyright
  16.  *    notice, this list of conditions and the following disclaimer.
  17.  * 2. Redistributions in binary form must reproduce the above copyright
  18.  *    notice, this list of conditions and the following disclaimer in the
  19.  *    documentation and/or other materials provided with the distribution.
  20.  * 3. All modifications to the source code must be clearly marked as
  21.  *    such.  Binary redistributions based on modified source code
  22.  *    must be clearly marked as modified versions in the documentation
  23.  *    and/or other materials provided with the distribution.
  24.  * 4. All advertising materials mentioning features or use of this software
  25.  *    must display the following acknowledgment:
  26.  *      This product includes software developed by Geoff Kuenning and
  27.  *      other unpaid contributors.
  28.  * 5. The name of Geoff Kuenning may not be used to endorse or promote
  29.  *    products derived from this software without specific prior
  30.  *    written permission.
  31.  *
  32.  * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
  33.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  34.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35.  * ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
  36.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  37.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  38.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  40.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  41.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  42.  * SUCH DAMAGE.
  43.  */
  44.  
  45. /*
  46.  * $Log: parse.y,v $
  47.  * Revision 1.52  1994/11/21  07:03:03  geoff
  48.  * Get rid of the last vestiges of the "+" flag option.
  49.  *
  50.  * Revision 1.51  1994/11/02  06:56:13  geoff
  51.  * Remove the anyword feature, which I've decided is a bad idea.
  52.  *
  53.  * Revision 1.50  1994/10/25  05:46:22  geoff
  54.  * Add support for the "+" (any word) flag modifier.
  55.  *
  56.  * Revision 1.49  1994/09/16  04:48:30  geoff
  57.  * Allow more than 128 string characters, by using different types in the
  58.  * appropriate places.
  59.  *
  60.  * Revision 1.48  1994/05/24  05:31:25  geoff
  61.  * Remember to convert the flag bit from a character to a bit number
  62.  * before putting it in the hash file.
  63.  *
  64.  * Revision 1.47  1994/05/24  04:54:33  geoff
  65.  * Improve the error checking for affix flag names, detecting bad flags
  66.  * and duplicates.
  67.  *
  68.  * Revision 1.46  1994/05/22  01:38:02  geoff
  69.  * Don't force flags to uppercase if lowercase flags are legal
  70.  *
  71.  * Revision 1.45  1994/05/17  06:44:17  geoff
  72.  * Add support for controlled compound formation and the COMPOUNDONLY
  73.  * option to affix flags.
  74.  *
  75.  * Revision 1.44  1994/02/07  05:51:03  geoff
  76.  * Fix a place where atoi got the wrong argument type (lint error only)
  77.  *
  78.  * Revision 1.43  1994/01/25  07:12:01  geoff
  79.  * Get rid of all old RCS log lines in preparation for the 3.1 release.
  80.  *
  81.  */
  82.  
  83. #include <ctype.h>
  84. #include "config.h"
  85. #include "ispell.h"
  86. #include "proto.h"
  87. #include "msgs.h"
  88.  
  89. %}
  90.  
  91. %union
  92.     {
  93.     int            simple;        /* Simple char or lval from yylex */
  94.     struct
  95.     {
  96.     char *        set;        /* Character set */
  97.     int        complement;    /* NZ if it is a complement set */
  98.     }
  99.             charset;
  100.     unsigned char *    string;        /* String */
  101.     ichar_t *        istr;        /* Internal string */
  102.     struct flagent *    entry;        /* Flag entry */
  103.     }
  104.  
  105. %{
  106.  
  107. static int    yylex P ((void)); /* Trivial lexical analyzer */
  108. static int    kwanalyze P ((int backslashed, unsigned char * str));
  109.                 /* Analyze a possible keyword */
  110. static void    getqstring P ((void));
  111.                 /* Get (double-)quoted string */
  112. static void    getrange P ((void)); /* Get a lexical character range */
  113. static int    backch P ((void)); /* Process a backslashed character */
  114. static void    yyerror P ((char * msg));
  115.                 /* Print out an error message */
  116. int        yyopen P ((char * file));
  117.                 /* Open a table file */
  118. void        yyinit P ((void)); /* Initialize for parsing */
  119. static int    grabchar P ((void));
  120.                 /* Get a character and track line number */
  121. static void    ungrabchar P ((int ch));
  122.                 /* Unget a character, tracking line numbers */
  123. static int    sufcmp P ((struct flagent * flag1, struct flagent * flag2));
  124.                 /* Compare suffix flags for qsort */
  125. static int    precmp P ((struct flagent * flag1, struct flagent * flag2));
  126.                 /* Compare prefix flags for qsort */
  127. static int    addstringchar P ((unsigned char * str, int lower, int upper));
  128.                 /* Add a string character to the table */
  129. static int    stringcharcmp P ((char * a, char * b));
  130.                 /* Strcmp() done right, for Sun 4's */
  131. #ifdef TBLDEBUG
  132. static void    tbldump P ((struct flagent * flagp, int numflags));
  133.                 /* Dump a flag table */
  134. static void    entdump P ((struct flagent * flagp));
  135.                 /* Dump one flag entry */
  136. static void    setdump P ((char * setp, int mask));
  137.                 /* Dump a set specification */
  138. static void    subsetdump P ((char * setp, int mask, int dumpval));
  139.                 /* Dump part of a set spec */
  140. #endif
  141.  
  142. struct kwtab
  143.     {
  144.     char *    kw;        /* Syntactic keyword */
  145.     int        val;        /* Lexical value */
  146.     };
  147.  
  148. #define TBLINC        10        /* Size to allocate table by */
  149.  
  150. static FILE *        aff_file = NULL; /* Input file pointer */
  151. static int        centnum;    /* Number of entries in curents */
  152. static int        centsize = 0;    /* Size of alloc'ed space in curents */
  153. static int        ctypechars;    /* Size of string in current strtype */
  154. static int        ctypenum = 0;    /* Number of entries in chartypes */
  155. static int        ctypesize = 0;    /* Size of alloc'ed spc in chartypes */
  156. static struct flagent * curents;    /* Current flag entry collection */
  157. static char *        fname = "(stdin)"; /* Current file name */
  158. static char        lexungrab[MAXSTRINGCHARLEN * 2]; /* Spc for ungrabch */
  159. static int        lineno = 1;    /* Current line number in file */
  160. static struct flagent * table;        /* Current table being built */
  161. static int        tblnum;        /* Numer of entries in table */
  162. static int        tblsize = 0;    /* Size of the flag table */
  163. static int        ungrablen;    /* Size of ungrab area */
  164. %}
  165.  
  166. %token <simple> '-'
  167. %token <simple> '>'
  168. %token <simple> ','
  169. %token <simple> ':'
  170. %token <simple> '.'
  171. %token <simple> '*'
  172. %token <simple> '~'
  173. %token <simple> ALLAFFIXES
  174. %token <simple> ALTSTRINGCHAR
  175. %token <simple> ALTSTRINGTYPE
  176. %token <simple> BOUNDARYCHARS
  177. %token <simple> COMPOUNDMIN
  178. %token <simple> COMPOUNDWORDS
  179. %token <simple> CONTROLLED
  180. %token <simple> DEFSTRINGTYPE
  181. %token <simple> FLAG
  182. %token <simple> FLAGMARKER
  183. %token <simple> NROFFCHARS
  184. %token <simple> OFF
  185. %token <simple> ON
  186. %token <simple> PREFIXES
  187. %token <charset> RANGE
  188. %token <simple> SUFFIXES
  189. %token <string> STRING
  190. %token <simple> STRINGCHAR
  191. %token <simple> TEXCHARS
  192. %token <simple> WORDCHARS
  193.  
  194. %type <simple> file
  195. %type <simple> headers
  196. %type <simple> option_group
  197. %type <simple> charset_group
  198. %type <simple> altchar_group
  199. %type <simple> charset_stmt
  200. %type <simple> option_stmt
  201. %type <simple> altchar_stmt
  202. %type <simple> altchar_spec_group
  203. %type <simple> altchar_spec
  204. %type <simple> deftype_stmt
  205. %type <string> stringtype_info
  206. %type <simple> filesuf_list
  207. %type <string> filesuf
  208. %type <charset> char_set
  209. %type <simple> tables
  210. %type <simple> prefix_table
  211. %type <simple> suffix_table
  212. %type <simple> table
  213. %type <simple> flagdef
  214. %type <simple> flagoptions
  215. %type <simple> flagoption
  216. %type <simple> error
  217. %type <simple> on_or_off
  218. %type <simple> rules
  219. %type <entry> affix_rule
  220. %type <entry> cond_or_null
  221. %type <entry> conditions
  222. %type <istr> ichar_string
  223. %%
  224. file        :    headers tables
  225.         |    tables
  226.         ;
  227.  
  228. headers        :    option_group charset_group
  229.         |    option_group charset_group altchar_group
  230.         |    charset_group
  231.         |    charset_group altchar_group
  232.         ;
  233.  
  234. option_group    :    option_stmt
  235.         |    option_group option_stmt
  236.         ;
  237.  
  238. charset_group    :    deftype_stmt charset_stmt
  239.         |    charset_stmt
  240.         |    charset_group charset_stmt
  241.         ;
  242.  
  243. deftype_stmt    :    DEFSTRINGTYPE stringtype_info
  244.         ;
  245.  
  246. altchar_group    :    altchar_stmt
  247.         |    altchar_group altchar_stmt
  248.         ;
  249.  
  250. charset_stmt     :    WORDCHARS char_set char_set
  251.                 {
  252.                 int nextlower;
  253.                 int nextupper;
  254.  
  255.                 for (nextlower = SET_SIZE + hashheader.nstrchars;
  256.                   --nextlower > SET_SIZE;
  257.                   )
  258.                 {
  259.                 if ($2.set[nextlower] != 0
  260.                   ||  $3.set[nextlower] != 0)
  261.                     {
  262.                     yyerror (PARSE_Y_NO_WORD_STRINGS);
  263.                     break;
  264.                     }
  265.                 }
  266.                 for (nextlower = 0;
  267.                   nextlower < SET_SIZE;
  268.                   nextlower++)
  269.                 {
  270.                 hashheader.wordchars[nextlower]
  271.                   |= $2.set[nextlower] | $3.set[nextlower];
  272.                 hashheader.lowerchars[nextlower]
  273.                   |= $2.set[nextlower];
  274.                 hashheader.upperchars[nextlower]
  275.                   |= $3.set[nextlower];
  276.                 }
  277.                 for (nextlower = nextupper = 0;
  278.                   nextlower < SET_SIZE;
  279.                   nextlower++)
  280.                 {
  281.                 if ($2.set[nextlower])
  282.                     {
  283.                     for (  ;
  284.                       nextupper < SET_SIZE
  285.                     &&  !$3.set[nextupper];
  286.                       nextupper++)
  287.                     ;
  288.                     if (nextupper == SET_SIZE)
  289.                     yyerror (PARSE_Y_UNMATCHED);
  290.                     else
  291.                     {
  292.                     hashheader.lowerconv[nextupper]
  293.                       = (ichar_t) nextlower;
  294.                     hashheader.upperconv[nextlower]
  295.                       = (ichar_t) nextupper;
  296.                     hashheader.sortorder[nextupper]
  297.                       = hashheader.sortval++;
  298.                     hashheader.sortorder[nextlower]
  299.                       = hashheader.sortval++;
  300.                     nextupper++;
  301.                     }
  302.                     }
  303.                 }
  304.                 for (  ;  nextupper < SET_SIZE;  nextupper++)
  305.                 {
  306.                 if ($3.set[nextupper])
  307.                     yyerror (PARSE_Y_UNMATCHED);
  308.                 }
  309.                 free ($2.set);
  310.                 free ($3.set);
  311.                 }
  312.         |    WORDCHARS char_set
  313.                 {
  314.                 int i;
  315.  
  316.                 for (i = SET_SIZE + hashheader.nstrchars;
  317.                   --i > SET_SIZE;
  318.                   )
  319.                 {
  320.                 if ($2.set[i] != 0)
  321.                     {
  322.                     yyerror (PARSE_Y_NO_WORD_STRINGS);
  323.                     break;
  324.                     }
  325.                 }
  326.                 for (i = 0;  i < SET_SIZE;  i++)
  327.                 {
  328.                 if ($2.set[i])
  329.                     {
  330.                     hashheader.wordchars[i] = 1;
  331.                     hashheader.sortorder[i]
  332.                       = hashheader.sortval++;
  333.                     }
  334.                 }
  335.                 free ($2.set);
  336.                 }
  337.         |    BOUNDARYCHARS char_set char_set
  338.                 {
  339.                 int nextlower;
  340.                 int nextupper;
  341.  
  342.                 for (nextlower = SET_SIZE + hashheader.nstrchars;
  343.                   --nextlower > SET_SIZE;
  344.                   )
  345.                 {
  346.                 if ($2.set[nextlower] != 0
  347.                   ||  $3.set[nextlower] != 0)
  348.                     {
  349.                     yyerror (PARSE_Y_NO_BOUNDARY_STRINGS);
  350.                     break;
  351.                     }
  352.                 }
  353.                 for (nextlower = 0;
  354.                   nextlower < SET_SIZE;
  355.                   nextlower++)
  356.                 {
  357.                 hashheader.boundarychars[nextlower]
  358.                   |= $2.set[nextlower] | $3.set[nextlower];
  359.                 hashheader.lowerchars[nextlower]
  360.                   |= $2.set[nextlower];
  361.                 hashheader.upperchars[nextlower]
  362.                   |= $3.set[nextlower];
  363.                 }
  364.                 for (nextlower = nextupper = 0;
  365.                   nextlower < SET_SIZE;
  366.                   nextlower++)
  367.                 {
  368.                 if ($2.set[nextlower])
  369.                     {
  370.                     for (  ;
  371.                       nextupper < SET_SIZE
  372.                     &&  !$3.set[nextupper];
  373.                       nextupper++)
  374.                     ;
  375.                     if (nextupper == SET_SIZE)
  376.                     yyerror (PARSE_Y_UNMATCHED);
  377.                     else
  378.                     {
  379.                     hashheader.lowerconv[nextupper]
  380.                       = (ichar_t) nextlower;
  381.                     hashheader.upperconv[nextlower]
  382.                       = (ichar_t) nextupper;
  383.                     hashheader.sortorder[nextupper]
  384.                       = hashheader.sortval++;
  385.                     hashheader.sortorder[nextlower]
  386.                       = hashheader.sortval++;
  387.                     nextupper++;
  388.                     }
  389.                     }
  390.                 }
  391.                 for (  ;  nextupper < SET_SIZE;  nextupper++)
  392.                 {
  393.                 if ($3.set[nextupper])
  394.                     yyerror (PARSE_Y_UNMATCHED);
  395.                 }
  396.                 free ($2.set);
  397.                 free ($3.set);
  398.                 }
  399.         |    BOUNDARYCHARS char_set
  400.                 {
  401.                 int i;
  402.  
  403.                 for (i = SET_SIZE + hashheader.nstrchars;
  404.                   --i > SET_SIZE;
  405.                   )
  406.                 {
  407.                 if ($2.set[i] != 0)
  408.                     {
  409.                     yyerror (PARSE_Y_NO_BOUNDARY_STRINGS);
  410.                     break;
  411.                     }
  412.                 }
  413.                 for (i = 0;  i < SET_SIZE;  i++)
  414.                 {
  415.                 if ($2.set[i])
  416.                     {
  417.                     hashheader.boundarychars[i] = 1;
  418.                     hashheader.sortorder[i]
  419.                       = hashheader.sortval++;
  420.                     }
  421.                 }
  422.                 free ($2.set);
  423.                 }
  424.         |    STRINGCHAR STRING
  425.                 {
  426.                 int len;
  427.  
  428.                 len = strlen ((char *) $2);
  429.                 if (len > MAXSTRINGCHARLEN)
  430.                 yyerror (PARSE_Y_LONG_STRING);
  431.                 else if (len == 0)
  432.                 yyerror (PARSE_Y_NULL_STRING);
  433.                 else if (hashheader.nstrchars >= MAXSTRINGCHARS)
  434.                 yyerror (PARSE_Y_MANY_STRINGS);
  435.                 else
  436.                 (void) addstringchar ($2, 0, 0);
  437.                 free ((char *) $2);
  438.                 }
  439.         |    STRINGCHAR STRING STRING
  440.                 {
  441.                 int lcslot;
  442.                 int len;
  443.                 int ucslot;
  444.  
  445.                 len = strlen ((char *) $2);
  446.                 if (strlen ((char *) $3) != len)
  447.                 yyerror (PARSE_Y_LENGTH_MISMATCH);
  448.                 else if (len > MAXSTRINGCHARLEN)
  449.                 yyerror (PARSE_Y_LONG_STRING);
  450.                 else if (len == 0)
  451.                 yyerror (PARSE_Y_NULL_STRING);
  452.                 else if (hashheader.nstrchars >= MAXSTRINGCHARS)
  453.                 yyerror (PARSE_Y_MANY_STRINGS);
  454.                 else
  455.                 {
  456.                 /*
  457.                  * Add the uppercase character first, so that
  458.                  * it will sort first.
  459.                  */
  460.                 lcslot = ucslot = addstringchar ($3, 0, 1);
  461.                 if (ucslot >= 0)
  462.                     lcslot = addstringchar ($2, 1, 0);
  463.                 if (ucslot >= 0  &&  lcslot >= 0)
  464.                     {
  465.                     if (ucslot >= lcslot)
  466.                     ucslot++;
  467.                     hashheader.lowerconv[ucslot] =
  468.                       (ichar_t) lcslot;
  469.                     hashheader.upperconv[lcslot] =
  470.                       (ichar_t) ucslot;
  471.                     }
  472.                 }
  473.                 free ((char *) $2);
  474.                 free ((char *) $3);
  475.                 }
  476.         ;
  477.  
  478. altchar_stmt    :    ALTSTRINGTYPE stringtype_info
  479.         |    ALTSTRINGTYPE stringtype_info altchar_spec_group
  480.         ;
  481.  
  482. stringtype_info    :    STRING STRING filesuf_list
  483.                 {
  484.                 chartypes[ctypenum].name = (char *) $1;
  485.                 chartypes[ctypenum].deformatter = (char *) $2;
  486.                 /*
  487.                  * Implement a few common synonyms.  This should
  488.                  * be generalized.
  489.                  */
  490.                 if (strcmp ((char *) $2, "TeX") == 0)
  491.                 (void) strcpy ((char *) $2, "tex");
  492.                 else if (strcmp ((char *) $2, "troff") == 0)
  493.                 (void) strcpy ((char *) $2, "nroff");
  494.                 /*
  495.                  * Someday, we'll accept generalized deformatters.
  496.                  * Then we can get rid of this test.
  497.                  */
  498.                 if (strcmp ((char *) $2, "nroff") != 0
  499.                   &&  strcmp ((char *) $2, "tex") != 0)
  500.                 yyerror (PARSE_Y_BAD_DEFORMATTER);
  501.                 ctypenum++;
  502.                 hashheader.nstrchartype = ctypenum;
  503.                 }
  504.         ;
  505.  
  506. filesuf_list    :    filesuf
  507.                 {
  508.                 if (ctypenum >= ctypesize)
  509.                 {
  510.                 if (ctypesize == 0)
  511.                     chartypes = (struct strchartype *)
  512.                       malloc (TBLINC
  513.                     * sizeof (struct strchartype));
  514.                 else
  515.                     chartypes = (struct strchartype *)
  516.                       realloc ((char *) chartypes,
  517.                     (ctypesize + TBLINC)
  518.                       * sizeof (struct strchartype));
  519.                 if (chartypes == NULL)
  520.                     {
  521.                     yyerror (PARSE_Y_NO_SPACE);
  522.                     exit (1);
  523.                     }
  524.                 ctypesize += TBLINC;
  525.                 }
  526.                 ctypechars =
  527.                   TBLINC * (strlen ((char *) $1) + 1) + 1;
  528.                 chartypes[ctypenum].suffixes =
  529.                   malloc ((unsigned int) ctypechars);
  530.                 if (chartypes[ctypenum].suffixes == NULL)
  531.                 {
  532.                 yyerror (PARSE_Y_NO_SPACE);
  533.                 exit (1);
  534.                 }
  535.                 (void) strcpy (chartypes[ctypenum].suffixes,
  536.                  (char *) $1);
  537.                 chartypes[ctypenum].suffixes
  538.                 [strlen ((char *) $1) + 1]
  539.                   = '\0';
  540.                 free ((char *) $1);
  541.                 }
  542.         |    filesuf_list filesuf
  543.                 {
  544.                 char *    nexttype;
  545.                 int        offset;
  546.  
  547.                 for (nexttype = chartypes[ctypenum].suffixes;
  548.                   *nexttype != '\0';
  549.                   nexttype += strlen (nexttype) + 1)
  550.                 ;
  551.                 offset = nexttype - chartypes[ctypenum].suffixes;
  552.                 if ((int) (offset + strlen ((char *) $2) + 1)
  553.                    >= ctypechars)
  554.                 {
  555.                 ctypechars +=
  556.                   TBLINC * (strlen ((char *) $2) + 1);
  557.                 chartypes[ctypenum].suffixes =
  558.                   realloc (chartypes[ctypenum].suffixes,
  559.                     (unsigned int) ctypechars);
  560.                 if (chartypes[ctypenum].suffixes == NULL)
  561.                     {
  562.                     yyerror (PARSE_Y_NO_SPACE);
  563.                     exit (1);
  564.                     }
  565.                 nexttype =
  566.                   chartypes[ctypenum].suffixes + offset;
  567.                 }
  568.                 (void) strcpy (nexttype, (char *) $2);
  569.                 nexttype[strlen ((char *) $2) + 1] = '\0';
  570.                 free ((char *) $2);
  571.                 }
  572.         ;
  573.  
  574. filesuf        :    STRING
  575.         ;
  576.  
  577. altchar_spec_group
  578.         :    altchar_spec
  579.         |    altchar_spec_group altchar_spec
  580.         ;
  581.  
  582. altchar_spec    :    ALTSTRINGCHAR STRING STRING
  583.                 {
  584.                 int i;
  585.                 int len;
  586.                 int slot;
  587.  
  588.                 len = strlen ((char *) $2);
  589.                 if (len > MAXSTRINGCHARLEN)
  590.                 yyerror (PARSE_Y_LONG_STRING);
  591.                 else if (len == 0)
  592.                 yyerror (PARSE_Y_NULL_STRING);
  593.                 else if (hashheader.nstrchars >= MAXSTRINGCHARS)
  594.                 yyerror (PARSE_Y_MANY_STRINGS);
  595.                 else if (!isstringch ((char *) $3, 1))
  596.                 yyerror (PARSE_Y_NO_SUCH_STRING);
  597.                 else
  598.                 {
  599.                 slot = addstringchar ($2, 0, 0) - SET_SIZE;
  600.                 if (laststringch >= slot)
  601.                     laststringch++;
  602.                 hashheader.stringdups[slot] = laststringch;
  603.                 for (i = hashheader.nstrchars;  --i >= 0;  )
  604.                     {
  605.                     if (hashheader.stringdups[i]
  606.                       == laststringch)
  607.                     hashheader.dupnos[slot]++;
  608.                     }
  609.                 /*
  610.                  * The above code sets dupnos one too high,
  611.                  * because it counts the character itself.
  612.                  */
  613.                 if (hashheader.dupnos[slot]
  614.                   != hashheader.nstrchartype)
  615.                     yyerror (PARSE_Y_MULTIPLE_STRINGS);
  616.                 hashheader.dupnos[slot]--;
  617.                 }
  618.                 free ((char *) $2);
  619.                 free ((char *) $3);
  620.                 }
  621.         ;
  622.  
  623. option_stmt    :    NROFFCHARS STRING
  624.                 {
  625.                 if (strlen ((char *) $2)
  626.                   == sizeof (hashheader.nrchars))
  627.                 (void) bcopy ((char *) $2, hashheader.nrchars,
  628.                   sizeof (hashheader.nrchars));
  629.                 else
  630.                 yyerror (PARSE_Y_WRONG_NROFF);
  631.                 free ((char *) $2);
  632.                 }
  633.         ;
  634.         |    TEXCHARS STRING
  635.                 {
  636.                 if (strlen ((char *) $2)
  637.                   == sizeof (hashheader.texchars))
  638.                 (void) bcopy ((char *) $2, hashheader.texchars,
  639.                   sizeof (hashheader.texchars));
  640.                 else
  641.                 yyerror (PARSE_Y_WRONG_TEX);
  642.                 free ((char *) $2);
  643.                 }
  644.         |    COMPOUNDMIN STRING
  645.                 {
  646.                 unsigned char * digitp; /* Pointer to next digit */
  647.  
  648.                 for (digitp = $2;  *digitp != '\0';  digitp++)
  649.                 {
  650.                 if (*digitp <= '0'  ||  *digitp >= '9')
  651.                     {
  652.                     yyerror (PARSE_Y_BAD_NUMBER);
  653.                     break;
  654.                     }
  655.                 }
  656.                 hashheader.compoundmin = atoi ((char *) $2);
  657.                 }
  658.         |    COMPOUNDWORDS on_or_off
  659.                 {
  660.                 hashheader.compoundflag = $2;
  661.                 }
  662.         |    COMPOUNDWORDS CONTROLLED STRING
  663.                 {
  664.                 if (strlen ((char *) $3) != 1)
  665.                 yyerror (PARSE_Y_LONG_FLAG);
  666.                 else if (hashheader.compoundbit >= 0)
  667.                 yyerror (PARSE_Y_DOUBLE_COMPOUND);
  668.                 else
  669.                 {
  670.                 hashheader.compoundbit = (unsigned char) $3[0];
  671. #if MASKBITS <= 128
  672.                 hashheader.compoundbit &= 0x7f;
  673. #endif /* MASKBITS */
  674. #if MASKBITS <= 32
  675.                 if (islower (hashheader.compoundbit))
  676.                     hashheader.compoundbit =
  677.                       toupper (hashheader.compoundbit);
  678. #endif /* MASKBITS */
  679. #if MASKBITS <= 64
  680.                 if (!isalpha (hashheader.compoundbit))
  681.                     yyerror (PARSE_Y_BAD_FLAG);
  682. #endif /* MASKBITS */
  683.                 hashheader.compoundbit =
  684.                   CHARTOBIT (hashheader.compoundbit);
  685.                 }
  686.                 hashheader.compoundflag = COMPOUND_CONTROLLED;
  687.                 }
  688.         |    ALLAFFIXES on_or_off
  689.                 {
  690.                 hashheader.defhardflag = $2;
  691.                 }
  692.         |    FLAGMARKER STRING
  693.                 {
  694.                 if (strlen ((char *) $2) != 1)
  695.                 yyerror (PARSE_Y_LONG_FLAG);
  696.                 else
  697.                 hashheader.flagmarker = $2[0];
  698.                 free ((char *) $2);
  699.                 }
  700.         ;
  701.  
  702. char_set    :    '.'
  703.                 {
  704.                 int        i;
  705.                 char *    set;
  706.  
  707.                 set = malloc (SET_SIZE + MAXSTRINGCHARS);
  708.                 if (set == NULL)
  709.                 {
  710.                 yyerror (PARSE_Y_NO_SPACE);
  711.                 exit (1);
  712.                 }
  713.                 $$.set = set;
  714.                 for (i = SET_SIZE + MAXSTRINGCHARS;  --i >= 0;  )
  715.                 *set++ = 1;
  716.                 $$.complement = 0;
  717.                 }
  718.         |    STRING
  719.                 {
  720.                 int        setlen;
  721.  
  722.                 $$.set = malloc (SET_SIZE + MAXSTRINGCHARS);
  723.                 if ($$.set == NULL)
  724.                 {
  725.                 yyerror (PARSE_Y_NO_SPACE);
  726.                 exit (1);
  727.                 }
  728.                 (void) bzero ($$.set, SET_SIZE + MAXSTRINGCHARS);
  729.                 if (l1_isstringch ((char *) $1, setlen, 1))
  730.                 {
  731.                 if (setlen != strlen ((char *) $1))
  732.                     yyerror (PARSE_Y_NEED_BLANK);
  733.                 $$.set[SET_SIZE + laststringch] = 1;
  734.                 }
  735.                 else
  736.                 {
  737.                 if (strlen ((char *) $1) != 1)
  738.                     yyerror (PARSE_Y_NEED_BLANK);
  739.                 $$.set[*$1] = 1;
  740.                 }
  741.                 free ((char *) $1);
  742.                 $$.complement = 0;
  743.                 }
  744.         |    RANGE
  745.         ;
  746.  
  747. on_or_off    :    ON
  748.                 {
  749.                 $$ = 1;
  750.                 }
  751.         |    OFF
  752.                 {
  753.                 $$ = 0;
  754.                 }
  755.         ;
  756.  
  757. tables        :    prefix_table suffix_table
  758.         |    suffix_table prefix_table
  759.         |    prefix_table
  760.         |    suffix_table
  761.         ;
  762.  
  763. prefix_table    :    PREFIXES table
  764.                 {
  765.                 pflaglist = table;
  766.                 numpflags = tblnum;
  767.                 /*
  768.                  * Sort the flag table.  This is critical so
  769.                  * that ispell can build a correct index
  770.                  * table.  The idea is to put similar affixes
  771.                  * together.
  772.                  */
  773.                 qsort ((char *) table, (unsigned) tblnum,
  774.                   sizeof (*table),
  775.                   (int (*) P ((const void *, const void *)))
  776.                 precmp);
  777. #ifdef TBLDEBUG
  778.                 (void) fprintf (stderr, "prefixes\n");
  779.                 tbldump (table, tblnum);
  780. #endif
  781.                 tblsize = 0;
  782.                 }
  783.         ;
  784.  
  785. suffix_table    :    SUFFIXES table
  786.                 {
  787.                 sflaglist = table;
  788.                 numsflags = tblnum;
  789.                 /*
  790.                  * See comments on the prefix sort.
  791.                  */
  792.                 qsort ((char *) table, (unsigned) tblnum,
  793.                   sizeof (*table),
  794.                   (int (*) P ((const void *, const void *)))
  795.                 sufcmp);
  796. #ifdef TBLDEBUG
  797.                 (void) fprintf (stderr, "suffixes\n");
  798.                 tbldump (table, tblnum);
  799. #endif
  800.                 tblsize = 0;
  801.                 }
  802.         ;
  803.  
  804. table        :    flagdef
  805.                 {
  806.                 if (tblsize == 0)
  807.                 {
  808.                 tblsize = centnum + TBLINC;
  809.                 tblnum = 0;
  810.                 table = (struct flagent *)
  811.                   malloc (tblsize * (sizeof (struct flagent)));
  812.                 if (table == NULL)
  813.                     {
  814.                     yyerror (PARSE_Y_NO_SPACE);
  815.                     exit (1);
  816.                     }
  817.                 }
  818.                 else if (tblnum + centnum >= tblsize)
  819.                 {
  820.                 tblsize = tblnum + centnum + TBLINC;
  821.                 table = (struct flagent *)
  822.                   realloc ((char *) table,
  823.                     tblsize * (sizeof (struct flagent)));
  824.                 if (table == NULL)
  825.                     {
  826.                     yyerror (PARSE_Y_NO_SPACE);
  827.                     exit (1);
  828.                     }
  829.                 }
  830.                 for (tblnum = 0;  tblnum < centnum;  tblnum++)
  831.                 table[tblnum] = curents[tblnum];
  832.                 centnum = 0;
  833.                 }
  834.         |    table flagdef
  835.                 {
  836.                 int i;
  837.  
  838.                 if (tblnum + centnum >= tblsize)
  839.                 {
  840.                 tblsize = tblnum + centnum + TBLINC;
  841.                 table = (struct flagent *)
  842.                   realloc ((char *) table,
  843.                     tblsize * (sizeof (struct flagent)));
  844.                 if (table == NULL)
  845.                     {
  846.                     yyerror (PARSE_Y_NO_SPACE);
  847.                     exit (1);
  848.                     }
  849.                 }
  850.                 for (i = 0;  i < centnum;  i++)
  851.                 table[tblnum + i] = curents[i];
  852.                 tblnum += centnum;
  853.                 centnum = 0;
  854.                 }
  855.         ;
  856.  
  857. flagdef        :    FLAG STRING ':' rules
  858.                 {
  859.                 int flagbit;
  860.                 int i;
  861.  
  862.                 if (strlen ((char *) $2) != 1)
  863.                 yyerror (PARSE_Y_LONG_FLAG);
  864.                 flagbit = (unsigned char) $2[0];
  865. #if MASKBITS <= 128
  866.                 flagbit &= 0x7f;
  867. #endif /* MASKBITS */
  868. #if MASKBITS <= 32
  869.                 if (islower (flagbit))
  870.                 flagbit = toupper (flagbit);
  871. #endif /* MASKBITS */
  872. #if MASKBITS <= 64
  873.                 if (!isalpha (flagbit))
  874.                 yyerror (PARSE_Y_BAD_FLAG);
  875. #endif /* MASKBITS */
  876.                 flagbit = CHARTOBIT (flagbit);
  877.                 for (i = 0;  i < tblnum;  i++)
  878.                 {
  879.                 if (table[i].flagbit == flagbit)
  880.                     yyerror (PARSE_Y_DUP_FLAG);
  881.                 }
  882.                 for (i = 0;  i < centnum;  i++)
  883.                 {
  884.                 curents[i].flagbit = flagbit;
  885.                 curents[i].flagflags = 0;
  886.                 }
  887.                 free ((char *) $2);
  888.                 }
  889.         |    FLAG flagoptions STRING ':' rules
  890.                 {
  891.                 int flagbit;
  892.                 int i;
  893.  
  894.                 if (strlen ((char *) $3) != 1)
  895.                 yyerror (PARSE_Y_LONG_FLAG);
  896.                 flagbit = (unsigned char) $3[0];
  897. #if MASKBITS <= 128
  898.                 flagbit &= 0x7f;
  899. #endif /* MASKBITS */
  900. #if MASKBITS <= 32
  901.                 if (islower (flagbit))
  902.                 flagbit = toupper (flagbit);
  903. #endif /* MASKBITS */
  904. #if MASKBITS <= 64
  905.                 if (!isalpha (flagbit))
  906.                 yyerror (PARSE_Y_BAD_FLAG);
  907. #endif /* MASKBITS */
  908.                 flagbit = CHARTOBIT (flagbit);
  909.                 for (i = 0;  i < tblnum;  i++)
  910.                 {
  911.                 if (table[i].flagbit == flagbit)
  912.                     yyerror (PARSE_Y_DUP_FLAG);
  913.                 }
  914.                 for (i = 0;  i < centnum;  i++)
  915.                 {
  916.                 curents[i].flagbit = flagbit;
  917.                 curents[i].flagflags = $2;
  918.                 }
  919.                 free ((char *) $3);
  920.                 }
  921.         |    error
  922.                 { $$ = 0; }
  923.         ;
  924.  
  925. flagoptions    :    flagoption
  926.         |    flagoptions flagoption
  927.                 {
  928.                 $$ = $1 | $2;
  929.                 }
  930.         ;
  931.  
  932. flagoption    :    '*'
  933.                 { $$ = FF_CROSSPRODUCT; }
  934.         |    '~'
  935.                 { $$ = FF_COMPOUNDONLY; }
  936.         ;
  937.  
  938. rules        :    affix_rule
  939.                 {
  940.                 if (centsize == 0)
  941.                 {
  942.                 curents = (struct flagent *)
  943.                   malloc (TBLINC * (sizeof (struct flagent)));
  944.                 if (curents == NULL)
  945.                     {
  946.                     yyerror (PARSE_Y_NO_SPACE);
  947.                     exit (1);
  948.                     }
  949.                 centsize = TBLINC;
  950.                 }
  951.                 curents[0] = *$1;
  952.                 centnum = 1;
  953.                 free ((char *) $1);
  954.                 $$ = 0;
  955.                 }
  956.         |    rules affix_rule
  957.                 {
  958.                 if (centnum >= centsize)
  959.                 {
  960.                 centsize += TBLINC;
  961.                 curents = (struct flagent *)
  962.                   realloc ((char *) curents,
  963.                     centsize * (sizeof (struct flagent)));
  964.                 if (curents == NULL)
  965.                     {
  966.                     yyerror (PARSE_Y_NO_SPACE);
  967.                     exit (1);
  968.                     }
  969.                 }
  970.                 curents[centnum] = *$2;
  971.                 centnum++;
  972.                 free ((char *) $2);
  973.                 }
  974.         ;
  975.  
  976. affix_rule    :    cond_or_null '>' ichar_string
  977.                 {
  978.                 int        i;
  979.  
  980.                 $1->stripl = 0;
  981.                 $1->strip = NULL;
  982.                 $1->affl = icharlen ($3);
  983.                 $1->affix = $3;
  984.                 upcase ($3);
  985.                 /*
  986.                  * As a special optimization (and a
  987.                  * concession to those who prefer the syntax
  988.                  * that way), convert any single condition
  989.                  * that accepts all characters into no
  990.                  * condition at all.
  991.                  */
  992.                 if ($1->numconds == 1)
  993.                 {
  994.                 for (i = SET_SIZE + hashheader.nstrchars;
  995.                   --i >= 0;
  996.                   )
  997.                     {
  998.                     if (($1->conds[i] & 1) == 0)
  999.                     break;
  1000.                     }
  1001.                 if (i < 0)
  1002.                     $1->numconds = 0;
  1003.                 }
  1004.                 $$ = $1;
  1005.                 }
  1006.         |    cond_or_null '>' '-' ichar_string ',' ichar_string
  1007.                 {
  1008.                 int            i;
  1009.  
  1010.                 $1->stripl = icharlen ($4);
  1011.                 $1->strip = $4;
  1012.                 upcase ($4);
  1013.                 $1->affl = icharlen ($6);
  1014.                 $1->affix = $6;
  1015.                 upcase ($6);
  1016.                 /*
  1017.                  * Convert the syntax ". > -xxx,yyy" into
  1018.                  * " > -xxx,yyy", as in the code just above.
  1019.                  */
  1020.                 if ($1->numconds == 1)
  1021.                 {
  1022.                 for (i = SET_SIZE + hashheader.nstrchars;
  1023.                   --i >= 0;
  1024.                   )
  1025.                     {
  1026.                     if (($1->conds[i] & 1) == 0)
  1027.                     break;
  1028.                     }
  1029.                 if (i < 0)
  1030.                     $1->numconds = 0;
  1031.                 }
  1032.                 $$ = $1;
  1033.                 }
  1034.         |    cond_or_null '>' '-' ichar_string ',' '-'
  1035.                 {
  1036.                 int            i;
  1037.  
  1038.                 $1->stripl = icharlen ($4);
  1039.                 $1->strip = $4;
  1040.                 upcase ($4);
  1041.                 $1->affl = 0;
  1042.                 $1->affix = NULL;
  1043.                 /*
  1044.                  * Convert the syntax ". > -xxx," into
  1045.                  * " > -xxx,", as in the code just above.
  1046.                  */
  1047.                 if ($1->numconds == 1)
  1048.                 {
  1049.                 for (i = SET_SIZE + hashheader.nstrchars;
  1050.                   --i >= 0;
  1051.                   )
  1052.                     {
  1053.                     if (($1->conds[i] & 1) == 0)
  1054.                     break;
  1055.                     }
  1056.                 if (i < 0)
  1057.                     $1->numconds = 0;
  1058.                 }
  1059.                 $$ = $1;
  1060.                 }
  1061.         |    cond_or_null '>' '-' ',' '-'
  1062.                 {
  1063.                 int            i;
  1064.  
  1065.                 $1->stripl = 0;
  1066.                 $1->strip = NULL;
  1067.                 $1->affl = 0;
  1068.                 $1->affix = NULL;
  1069.                 /*
  1070.                  * Convert the syntax ". > -,-" into
  1071.                  * " > -,-", as in the code just above.
  1072.                  */
  1073.                 if ($1->numconds == 1)
  1074.                 {
  1075.                 for (i = SET_SIZE + hashheader.nstrchars;
  1076.                   --i >= 0;
  1077.                   )
  1078.                     {
  1079.                     if (($1->conds[i] & 1) == 0)
  1080.                     break;
  1081.                     }
  1082.                 if (i < 0)
  1083.                     $1->numconds = 0;
  1084.                 }
  1085.                 $$ = $1;
  1086.                 }
  1087.         ;
  1088.  
  1089. cond_or_null    :    /* Empty */
  1090.                 {
  1091.                 struct flagent *    ent;
  1092.  
  1093.                 ent = (struct flagent *)
  1094.                   malloc (sizeof (struct flagent));
  1095.                 if (ent == NULL)
  1096.                 {
  1097.                 yyerror (PARSE_Y_NO_SPACE);
  1098.                 exit (1);
  1099.                 }
  1100.                 ent->numconds = 0;
  1101.                 (void) bzero (ent->conds,
  1102.                    SET_SIZE + MAXSTRINGCHARS);
  1103.                 $$ = ent;
  1104.                 }
  1105.         |    conditions
  1106.         ;
  1107.  
  1108. conditions    :    char_set
  1109.                 {
  1110.                 struct flagent *    ent;
  1111.                 int            i;
  1112.  
  1113.                 ent = (struct flagent *)
  1114.                   malloc (sizeof (struct flagent));
  1115.                 if (ent == NULL)
  1116.                 {
  1117.                 yyerror (PARSE_Y_NO_SPACE);
  1118.                 exit (1);
  1119.                 }
  1120.                 ent->numconds = 1;
  1121.                 (void) bzero (ent->conds,
  1122.                    SET_SIZE + MAXSTRINGCHARS);
  1123.                 /*
  1124.                  * Copy conditions to the new entry, making
  1125.                  * sure that uppercase versions are generated
  1126.                  * for lowercase input.
  1127.                  */
  1128.                 for (i = SET_SIZE + MAXSTRINGCHARS;  --i >= 0;  )
  1129.                 {
  1130.                 if ($1.set[i])
  1131.                     {
  1132.                     ent->conds[i] = 1;
  1133.                     if (!$1.complement)
  1134.                     ent->conds[mytoupper ((ichar_t) i)] = 1;
  1135.                     }
  1136.                 }
  1137.                 if ($1.complement)
  1138.                 {
  1139.                 for (i = SET_SIZE + MAXSTRINGCHARS;
  1140.                 --i >= 0;
  1141.                 )
  1142.                     {
  1143.                     if ($1.set[i] == 0)
  1144.                     ent->conds[mytoupper ((ichar_t) i)] = 0;
  1145.                     }
  1146.                 }
  1147.                 free ($1.set);
  1148.                 $$ = ent;
  1149.                 }
  1150.         |    conditions char_set
  1151.                 {
  1152.                 int            i;
  1153.                 int            mask;
  1154.  
  1155.                 if ($1->numconds >= 8)
  1156.                 {
  1157.                 yyerror (PARSE_Y_MANY_CONDS);
  1158.                 $1->numconds = 7;
  1159.                 }
  1160.                 mask = 1 << $1->numconds;
  1161.                 $1->numconds++;
  1162.                 for (i = SET_SIZE + MAXSTRINGCHARS;
  1163.                   --i >= 0;
  1164.                   )
  1165.                 {
  1166.                 if ($2.set[i])
  1167.                     {
  1168.                     $1->conds[i] |= mask;
  1169.                     if (!$2.complement)
  1170.                     $1->conds[mytoupper ((ichar_t) i)]
  1171.                       |= mask;
  1172.                     }
  1173.                 }
  1174.                 if ($2.complement)
  1175.                 {
  1176.                 mask = ~mask;
  1177.                 for (i = SET_SIZE + MAXSTRINGCHARS;
  1178.                   --i >= 0;
  1179.                   )
  1180.                     {
  1181.                     if ($2.set[i] == 0)
  1182.                     $1->conds[mytoupper ((ichar_t) i)]
  1183.                       &= mask;
  1184.                     }
  1185.                 }
  1186.                 free ($2.set);
  1187.                 }
  1188.         ;
  1189.  
  1190. ichar_string    :    STRING
  1191.                 {
  1192.                 ichar_t *tichar;
  1193.  
  1194.                 tichar = strtosichar ((char *) $1, 1);
  1195.                 $$ = (ichar_t *) malloc (sizeof (ichar_t)
  1196.                   * (icharlen (tichar) + 1));
  1197.                 if ($$ == NULL)
  1198.                 {
  1199.                 yyerror (PARSE_Y_NO_SPACE);
  1200.                 exit (1);
  1201.                 }
  1202.                 (void) icharcpy ($$, tichar);
  1203.                 free ((char *) $1);
  1204.                 }
  1205.         ;
  1206. %%
  1207. static struct kwtab            /* Table of built-in keywords */
  1208.         keywords[] =
  1209.     {
  1210.     {"allaffixes", ALLAFFIXES},
  1211.     {"altstringchar", ALTSTRINGCHAR},
  1212.     {"altstringtype", ALTSTRINGTYPE},
  1213.     {"boundarychars", BOUNDARYCHARS},
  1214.     {"compoundmin", COMPOUNDMIN},
  1215.     {"compoundwords", COMPOUNDWORDS},
  1216.     {"controlled", CONTROLLED},
  1217.     {"defstringtype", DEFSTRINGTYPE},
  1218.     {"flag", FLAG},
  1219.     {"flagmarker", FLAGMARKER},
  1220.     {"nroffchars", NROFFCHARS},
  1221.     {"troffchars", NROFFCHARS},
  1222.     {"on", ON},
  1223.     {"off", OFF},
  1224.     {"prefixes", PREFIXES},
  1225.     {"stringchar", STRINGCHAR},
  1226.     {"suffixes", SUFFIXES},
  1227.     {"TeXchars", TEXCHARS},
  1228.     {"texchars", TEXCHARS},
  1229.     {"wordchars", WORDCHARS},
  1230.     {NULL, 0}
  1231.     };
  1232.  
  1233. /*
  1234.  * Trivial lexical analyzer.
  1235.  */
  1236. static int yylex ()
  1237.     {
  1238.     int            backslashed; /* NZ if backslash appeared */
  1239.     register int    ch;    /* Next character seen */
  1240.     register unsigned char *
  1241.             lexp;    /* Pointer into lexstring */
  1242.     unsigned char    lexstring[256];    /* Space for collecting strings */
  1243.  
  1244.     while ((ch = grabchar ()) != EOF  &&  (isspace (ch)  ||  ch == '#'))
  1245.     {            /* Skip whitespace and comments */
  1246.     if (ch == '#')
  1247.         {
  1248.         while ((ch = grabchar ()) != EOF  &&  ch != '\n')
  1249.         ;
  1250.         }
  1251.     }
  1252.     switch (ch)
  1253.     {
  1254.     case EOF:
  1255.         return EOF;
  1256.     case '"':
  1257.         getqstring ();
  1258.         return STRING;
  1259.     case '-':
  1260.     case '>':
  1261.     case ',':
  1262.     case ':':
  1263.     case '.':
  1264.     case '*':
  1265.     case '~':
  1266.         yylval.simple = ch;
  1267.         return ch;
  1268.     case '[':        /* Beginning of a range set ] */
  1269.         getrange ();    /* Get the range */
  1270.         return RANGE;
  1271.     }
  1272.     /*
  1273.      * We get here if the character is an ordinary one;  note that
  1274.      * this includes backslashes.
  1275.      */
  1276.     backslashed = 0;
  1277.     lexp = lexstring;
  1278.     for (  ;  ;  )
  1279.     {
  1280.     switch (ch)
  1281.         {
  1282.         case EOF:
  1283.         *lexp = '\0';
  1284.         return kwanalyze (backslashed, lexstring);
  1285.         case '\\':
  1286.         backslashed = 1;
  1287.         ch = backch ();
  1288.         *lexp++ = (char) ch;
  1289.         break;
  1290.         case ' ':
  1291.         case '\t':
  1292.         case '\n':
  1293.         case '\f':
  1294.         case '\r':
  1295.         *lexp = '\0';
  1296.         return kwanalyze (backslashed, lexstring);
  1297.         case '#':
  1298.         case '>':
  1299.         case ':':
  1300.         case '-':
  1301.         case ',':
  1302.         case '[':            /* ] */
  1303.         ungrabchar (ch);
  1304.         *lexp = '\0';
  1305.         return kwanalyze (backslashed, lexstring);
  1306.         default:
  1307.         *lexp++ = (char) ch;
  1308. #ifdef NO8BIT
  1309.         if (ch & 0x80)
  1310.             yyerror (PARSE_Y_8_BIT);
  1311. #endif /* NO8BIT */
  1312.         break;
  1313.         }
  1314.     ch = grabchar ();
  1315.     }
  1316.     }
  1317.  
  1318. static int kwanalyze (backslashed, str)
  1319.     int            backslashed;    /* NZ if string had a backslash */
  1320.     register unsigned char *
  1321.             str;        /* String to analyze */
  1322.     {
  1323.     register struct kwtab *
  1324.             kwptr;        /* Pointer into keyword table */
  1325.  
  1326.     yylval.simple = 0;
  1327.     if (!backslashed)            /* Backslash means not keyword */
  1328.     {
  1329.     for (kwptr = keywords;  kwptr->kw != NULL;  kwptr++)
  1330.         {
  1331.         if (strcmp (kwptr->kw, (char *) str) == 0)
  1332.         return (yylval.simple = kwptr->val);
  1333.         }
  1334.     }
  1335.     yylval.string =
  1336.       (unsigned char *) malloc ((unsigned) strlen ((char *) str) + 1);
  1337.     if (yylval.string == NULL)
  1338.     {
  1339.     yyerror (PARSE_Y_NO_SPACE);
  1340.     exit (1);
  1341.     }
  1342.     (void) strcpy ((char *) yylval.string, (char *) str);
  1343. #ifdef NO8BIT
  1344.     while (*str != '\0')
  1345.     {
  1346.     if (*str++ & 0x80)
  1347.         yyerror (PARSE_Y_8_BIT);
  1348.     }
  1349. #endif /* NO8BIT */
  1350.     return STRING;
  1351.     }
  1352.  
  1353. /*
  1354.  * Analyze a string in double quotes.  The leading quote has already
  1355.  * been processed.
  1356.  */
  1357. static void getqstring ()
  1358.     {
  1359.     register int    ch;        /* Next character read */
  1360.     char        lexstring[256];    /* Room to collect the string */
  1361.     register char *    lexp;        /* Pointer into lexstring */
  1362.  
  1363.     for (lexp = lexstring;
  1364.       (ch = grabchar ()) != EOF  &&  ch != '"'
  1365.     &&  lexp < &lexstring[sizeof lexstring - 1];
  1366.       )
  1367.     {
  1368.     if (ch == '\\')
  1369.         ch = backch ();
  1370.     *lexp++ = (char) ch;
  1371.     }
  1372.     *lexp++ = '\0';
  1373.     if (ch == EOF)
  1374.     yyerror (PARSE_Y_EOF);
  1375.     else if (ch != '"')
  1376.     {
  1377.     yyerror (PARSE_Y_LONG_QUOTE);
  1378.     while ((ch = grabchar ()) != EOF  &&  ch != '"')
  1379.         {
  1380.         if (ch == '\\')
  1381.         ch = backch ();
  1382.         }
  1383.     }
  1384.     yylval.string = (unsigned char *) malloc ((unsigned) (lexp - lexstring));
  1385.     if (yylval.string == NULL)
  1386.     {
  1387.     yyerror (PARSE_Y_NO_SPACE);
  1388.     exit (1);
  1389.     }
  1390.     (void) strcpy ((char *) yylval.string, lexstring);
  1391. #ifdef NO8BIT
  1392.     for (lexp = lexstring;  *lexp != '\0';  )
  1393.     {
  1394.     if (*lexp++ & 0x80)
  1395.         yyerror (PARSE_Y_8_BIT);
  1396.     }
  1397. #endif /* NO8BIT */
  1398.     }
  1399.  
  1400. /*
  1401.  * Analyze a range (e.g., [A-Za-z]).  The left square bracket
  1402.  * has already been processed.
  1403.  */
  1404. static void getrange ()            /* Parse a range set */
  1405.     {
  1406.     register int    ch;        /* Next character read */
  1407.     register int    lastch;        /* Previous char, for ranges */
  1408.     char        stringch[MAXSTRINGCHARLEN];
  1409.     int            stringchlen;
  1410.  
  1411.     yylval.charset.set = malloc (SET_SIZE + MAXSTRINGCHARS);
  1412.     if (yylval.charset.set == NULL)
  1413.     {
  1414.     yyerror (PARSE_Y_NO_SPACE);
  1415.     exit (1);
  1416.     }
  1417.  
  1418.     /* Start with a null set */
  1419.     (void) bzero (yylval.charset.set, SET_SIZE + MAXSTRINGCHARS);
  1420.     yylval.charset.complement = 0;
  1421.  
  1422.     lastch = -1;
  1423.     ch = grabchar ();
  1424.     if (ch == '^')
  1425.     {
  1426.     yylval.charset.complement = 1;
  1427.     ch = grabchar ();
  1428.     }
  1429.     /* [ */
  1430.     if (ch == ']')
  1431.     {
  1432.     /* [[ */
  1433.     lastch = ']';
  1434.     yylval.charset.set[']'] = 1;
  1435.     }
  1436.     else
  1437.     ungrabchar (ch);
  1438.     /* [ */
  1439.     while ((ch = grabchar ()) != EOF  &&  ch != ']')
  1440.     {
  1441.     if (isstringstart (ch))        /* Handle a possible string character */
  1442.         {
  1443.         stringch[0] = (char) ch;
  1444.         for (stringchlen = 1;
  1445.           stringchlen < MAXSTRINGCHARLEN;
  1446.           stringchlen++)
  1447.         {
  1448.         stringch[stringchlen] = '\0';
  1449.         if (isstringch (stringch, 1))
  1450.             {
  1451.             yylval.charset.set[SET_SIZE + laststringch] = 1;
  1452.             stringchlen = 0;
  1453.             break;
  1454.             }
  1455.         ch = grabchar ();
  1456.         if (ch == EOF)
  1457.             break;
  1458.         else
  1459.             stringch[stringchlen] = (char) ch;
  1460.         }
  1461.         if (stringchlen == 0)
  1462.         {
  1463.         lastch = -1;        /* String characters can't be ranges */
  1464.         continue;        /* We found a string character */
  1465.         }
  1466.         /*
  1467.          * Not a string character - put it back
  1468.          */
  1469.         while (--stringchlen > 0)
  1470.         ungrabchar (stringch[stringchlen] & 0xFF);
  1471.         ch = stringch[0] & 0xFF;
  1472.         }
  1473.     if (ch == '\\')
  1474.         {
  1475.         lastch = ch = backch ();
  1476.         yylval.charset.set[ch] = 1;
  1477.         continue;
  1478.         }
  1479. #ifdef NO8BIT
  1480.     if (ch & 0x80)
  1481.         {
  1482.         yyerror (PARSE_Y_8_BIT);
  1483.         ch &= 0x7F;
  1484.         }
  1485. #endif /* NO8BIT */
  1486.     if (ch == '-')            /* Handle a range */
  1487.         {
  1488.         if (lastch == -1)
  1489.         {
  1490.         lastch = ch = '-';    /* Not really a range */
  1491.         yylval.charset.set['-'] = 1;
  1492.         }
  1493.         else
  1494.         {
  1495.         ch = grabchar ();
  1496.         /* [ */
  1497.         if (ch == EOF  ||  ch == ']')
  1498.             {
  1499.             lastch = ch = '-';    /* Not really range */
  1500.             yylval.charset.set['-'] = 1;
  1501.             if (ch != EOF)
  1502.             ungrabchar (ch);
  1503.             }
  1504.         else
  1505.             {
  1506. #ifdef NO8BIT
  1507.             if (ch & 0x80)
  1508.             {
  1509.             yyerror (PARSE_Y_8_BIT);
  1510.             ch &= 0x7F;
  1511.             }
  1512. #endif /* NO8BIT */
  1513.             if (ch == '\\')
  1514.             ch = backch ();
  1515.             while (lastch <= ch)
  1516.             yylval.charset.set[lastch++] = 1;
  1517.             lastch = -1;
  1518.             }
  1519.         }
  1520.         }
  1521.     else
  1522.         {
  1523.         lastch = ch;
  1524.         yylval.charset.set[ch] = 1;
  1525.         }
  1526.     }
  1527.     if (yylval.charset.complement)
  1528.     {
  1529.     for (ch = 0;  ch < SET_SIZE + MAXSTRINGCHARS;  ch++)
  1530.         yylval.charset.set[ch] = !yylval.charset.set[ch];
  1531.     }
  1532.     }
  1533.  
  1534. static int backch ()            /* Process post-backslash characters */
  1535.     {
  1536.     register int    ch;        /* Next character read */
  1537.     register int    octval;        /* Budding octal value */
  1538.  
  1539.     ch = grabchar ();
  1540.     if (ch == EOF)
  1541.     return '\\';
  1542.     else if (ch >= '0'  &&  ch <= '7')
  1543.     {
  1544.     octval = ch - '0';
  1545.     ch = grabchar ();
  1546.     if (ch >= '0'  &&  ch <= '7')
  1547.         {
  1548.         octval = (octval << 3) + ch - '0';
  1549.         ch = grabchar ();
  1550.         if (ch >= '0'  &&  ch <= '7')
  1551.         octval = (octval << 3) + ch - '0';
  1552.         else
  1553.         ungrabchar (ch);
  1554.         }
  1555.     else if (ch != EOF)
  1556.         ungrabchar (ch);
  1557.     ch = octval;
  1558.     }
  1559.     else if (ch == 'x')
  1560.     {
  1561.     ch = grabchar ();
  1562.     octval = 0;
  1563.     if ((ch >= '0'  &&  ch <= '9')
  1564.       ||  (ch >= 'a'  &&  ch <= 'f')
  1565.       ||  (ch >= 'A'  &&  ch <= 'F'))
  1566.         {
  1567.         if (ch >= '0'  &&  ch <= '9')
  1568.         octval = ch - '0';
  1569.         else if (ch >= 'a'  &&  ch <= 'f')
  1570.         octval = ch - 'a' + 0xA;
  1571.         else if (ch >= 'A'  &&  ch <= 'F')
  1572.         octval = ch - 'A' + 0xA;
  1573.         ch = grabchar ();
  1574.         octval <<= 4;
  1575.         if (ch >= '0'  &&  ch <= '9')
  1576.         octval |= ch -'0';
  1577.         else if (ch >= 'a'  &&  ch <= 'f')
  1578.         octval |= ch - 'a' + 0xA;
  1579.         else if (ch >= 'A'  &&  ch <= 'F')
  1580.         octval |= ch - 'A' + 0xA;
  1581.         else if (ch != EOF)
  1582.         {
  1583.         octval >>= 4;
  1584.         ungrabchar (ch);
  1585.         }
  1586.         }
  1587.     else if (ch != EOF)
  1588.         ungrabchar (ch);
  1589.     ch = octval;
  1590.     }
  1591.     else
  1592.     {
  1593.     switch (ch)
  1594.         {
  1595.         case 'n':
  1596.         ch = '\n';
  1597.         break;
  1598.         case 'f':
  1599.         ch = '\f';
  1600.         break;
  1601.         case 'r':
  1602.         ch = '\r';
  1603.         break;
  1604.         case 'b':
  1605.         ch = '\b';
  1606.         break;
  1607.         case 't':
  1608.         ch = '\t';
  1609.         break;
  1610.         case 'v':
  1611.         ch = '\v';
  1612.         break;
  1613.         }
  1614.     }
  1615. #ifdef NO8BIT
  1616.     if (ch & 0x80)
  1617.     {
  1618.     yyerror (PARSE_Y_8_BIT);
  1619.     ch &= 0x7F;
  1620.     }
  1621. #endif /* NO8BIT */
  1622.     return ch;
  1623.     }
  1624.  
  1625. static void yyerror (str)
  1626.     char *        str;    /* Error string */
  1627.     {
  1628.     (void) fflush (stdout);
  1629.     (void) fprintf (stderr, PARSE_Y_ERROR_FORMAT(fname, lineno, str));
  1630.     (void) fflush (stderr);
  1631.     }
  1632.  
  1633. int yyopen (file)
  1634.     register char *    file;    /* File name to be opened */
  1635.     {
  1636.     fname = malloc ((unsigned) strlen (file) + 1);
  1637.     if (fname == NULL)
  1638.     {
  1639.     (void) fprintf (stderr, PARSE_Y_MALLOC_TROUBLE);
  1640.     exit (1);
  1641.     }
  1642.     (void) strcpy (fname, file);
  1643.     aff_file = fopen (file, "r");
  1644.     if (aff_file == NULL)
  1645.     {
  1646.     (void) fprintf (stderr, CANT_OPEN, file);
  1647.     perror ("");
  1648.     return 1;
  1649.     }
  1650.     lineno = 1;
  1651.     return 0;
  1652.     }
  1653.  
  1654. void yyinit ()
  1655.     {
  1656.     register unsigned int i;    /* Loop counter */
  1657.  
  1658.     if (aff_file == NULL)
  1659.     aff_file = stdin;    /* Must be dynamically initialized on Amigas */
  1660.     for (i = 0;  i < SET_SIZE + MAXSTRINGCHARS;  i++)
  1661.     {
  1662.     hashheader.lowerconv[i] = (ichar_t) i;
  1663.     hashheader.upperconv[i] = (ichar_t) i;
  1664.     hashheader.wordchars[i] = 0;
  1665.     hashheader.lowerchars[i] = 0;
  1666.     hashheader.upperchars[i] = 0;
  1667.     hashheader.boundarychars[i] = 0;
  1668.     /*
  1669.      * The default sort order is a big value so that there is room
  1670.      * to insert "underneath" it.  In this way, special characters
  1671.      * will sort last, but in ASCII order.
  1672.      */
  1673.     hashheader.sortorder[i] = i + 1 + 2 * SET_SIZE;
  1674.     }
  1675.     for (i = 0;  i < SET_SIZE;  i++)
  1676.     hashheader.stringstarts[i] = 0;
  1677.     for (i = 0;  i < MAXSTRINGCHARS;  i++)
  1678.     {
  1679.     hashheader.stringdups[i] = i;
  1680.     hashheader.dupnos[i] = 0;
  1681.     }
  1682.     
  1683.     hashheader.sortval = 1;    /* This is so 0 can mean uninitialized */
  1684.     (void) bcopy (NRSPECIAL, hashheader.nrchars, sizeof hashheader.nrchars);
  1685.     (void) bcopy (TEXSPECIAL, hashheader.texchars, sizeof hashheader.texchars);
  1686.     hashheader.compoundflag = COMPOUND_NEVER; /* Dflt is report missing blks */
  1687.     hashheader.defhardflag = 0; /* Default is to try hard only if failures */
  1688.     hashheader.nstrchars = 0;    /* No string characters to start with */
  1689.     hashheader.flagmarker = '/'; /* Default flag marker is slash */
  1690.     hashheader.compoundmin = 3;    /* Dflt is at least 3 chars in cmpnd parts */
  1691.     hashheader.compoundbit = -1; /* Dflt is no compound bit */
  1692.     /* Set up magic numbers and compile options */
  1693.     hashheader.magic = hashheader.magic2 = MAGIC;
  1694.     hashheader.compileoptions = COMPILEOPTIONS;
  1695.     hashheader.maxstringchars = MAXSTRINGCHARS;
  1696.     hashheader.maxstringcharlen = MAXSTRINGCHARLEN;
  1697.     }
  1698.  
  1699. static int grabchar ()        /* Get a character and count lines */
  1700.     {
  1701.     int            ch;    /* Next input character */
  1702.  
  1703.     if (ungrablen > 0)
  1704.     ch = lexungrab[--ungrablen] & 0xFF;
  1705.     else
  1706.     ch = getc (aff_file);
  1707.     if (ch == '\n')
  1708.     lineno++;
  1709.     return ch;
  1710.     }
  1711.  
  1712. static void ungrabchar (ch)    /* Unget a character, tracking line numbers */
  1713.     int            ch;    /* Character to put back */
  1714.     {
  1715.  
  1716.     if (ch == '\n')
  1717.     lineno--;
  1718.     if (ch != EOF)
  1719.     {
  1720.     if (ungrablen == sizeof (lexungrab))
  1721.         yyerror (PARSE_Y_UNGRAB_PROBLEM);
  1722.     else
  1723.         lexungrab[ungrablen++] = (char) ch;
  1724.     }
  1725.     }
  1726.  
  1727. static int sufcmp (flag1, flag2)    /* Compare suffix flags for qsort */
  1728.     register struct flagent *    flag1;    /* Flags to be compared */
  1729.     register struct flagent *    flag2;    /* ... */
  1730.     {
  1731.     register ichar_t *        cp1;    /* Pointer into flag1's suffix */
  1732.     register ichar_t *        cp2;    /* Pointer into flag2's suffix */
  1733.  
  1734.     if (flag1->affl == 0  ||  flag2->affl == 0)
  1735.     return flag1->affl - flag2->affl;
  1736.     cp1 = flag1->affix + flag1->affl;
  1737.     cp2 = flag2->affix + flag2->affl;
  1738.     while (*--cp1 == *--cp2  &&  cp1 > flag1->affix  &&  cp2 > flag2->affix)
  1739.     ;
  1740.     if (*cp1 == *cp2)
  1741.     {
  1742.     if (cp1 == flag1->affix)
  1743.         {
  1744.         if (cp2 == flag2->affix)
  1745.         return 0;
  1746.         else
  1747.         return -1;
  1748.         }
  1749.     else
  1750.         return 1;
  1751.     }
  1752.     return *cp1 - *cp2;
  1753.     }
  1754.  
  1755. static int precmp (flag1, flag2)    /* Compare prefix flags for qsort */
  1756.     register struct flagent *    flag1;    /* Flags to be compared */
  1757.     register struct flagent *    flag2;    /* ... */
  1758.     {
  1759.  
  1760.     if (flag1->affl == 0  ||  flag2->affl == 0)
  1761.     return flag1->affl - flag2->affl;
  1762.     else
  1763.     return icharcmp (flag1->affix, flag2->affix);
  1764.     }
  1765.  
  1766. static int addstringchar (str, lower, upper) /* Add a string character */
  1767.     register unsigned char *    str;    /* String character to be added */
  1768.     int                lower;    /* NZ if a lower string */
  1769.     int                upper;    /* NZ if an upper string */
  1770.     {
  1771.     int                len;    /* Length of the string */
  1772.     register unsigned int    mslot;    /* Slot being moved or modified */
  1773.     register unsigned int    slot;    /* Where to put it */
  1774.  
  1775.     len = strlen ((char *) str);
  1776.     if (len > MAXSTRINGCHARLEN)
  1777.     {
  1778.     yyerror (PARSE_Y_LONG_STRING);
  1779.     }
  1780.     else if (len == 0)
  1781.     {
  1782.     yyerror (PARSE_Y_NULL_STRING);
  1783.     return -1;
  1784.     }
  1785.     else if (hashheader.nstrchars >= MAXSTRINGCHARS)
  1786.     {
  1787.     yyerror (PARSE_Y_MANY_STRINGS);
  1788.     return -1;
  1789.     }
  1790.  
  1791.     /*
  1792.      * Find where to put the new character
  1793.      */
  1794.     for (slot = 0;  slot < hashheader.nstrchars;  slot++)
  1795.     {
  1796.     if (stringcharcmp (&hashheader.stringchars[slot][0], (char *) str) > 0)
  1797.         break;
  1798.     }
  1799.     /*
  1800.      * Fix all duplicate numbers to reflect the new slot.
  1801.      */
  1802.     for (mslot = 0;  mslot < hashheader.nstrchars;  mslot++)
  1803.     {
  1804.     if (hashheader.stringdups[mslot] >= slot)
  1805.         hashheader.stringdups[mslot]++;
  1806.     }
  1807.     /*
  1808.      * Fix all characters before it so that their case conversion reflects
  1809.      * the new locations of the characters that will follow the new one.
  1810.      */
  1811.     slot += SET_SIZE;
  1812.     for (mslot = SET_SIZE;  mslot < slot;  mslot++)
  1813.     {
  1814.     if (hashheader.lowerconv[mslot] >= (ichar_t) slot)
  1815.         hashheader.lowerconv[mslot]++;
  1816.     if (hashheader.upperconv[mslot] >= (ichar_t) slot)
  1817.         hashheader.upperconv[mslot]++;
  1818.     }
  1819.     /*
  1820.      * Slide up all the other characters to make room for the new one, also
  1821.      * making the appropriate changes in the case-conversion tables.
  1822.      */
  1823.     for (mslot = hashheader.nstrchars + SET_SIZE;  --mslot >= slot;  )
  1824.     {
  1825.     (void) strcpy (&hashheader.stringchars[mslot + 1 - SET_SIZE][0],
  1826.       &hashheader.stringchars[mslot - SET_SIZE][0]);
  1827.     hashheader.lowerchars[mslot + 1] = hashheader.lowerchars[mslot];
  1828.     hashheader.upperchars[mslot + 1] = hashheader.upperchars[mslot];
  1829.     hashheader.wordchars[mslot + 1] = hashheader.wordchars[mslot];
  1830.     hashheader.boundarychars[mslot + 1] = hashheader.boundarychars[mslot];
  1831.     if (hashheader.lowerconv[mslot] >= (ichar_t) slot)
  1832.         hashheader.lowerconv[mslot]++;
  1833.     if (hashheader.upperconv[mslot] >= (ichar_t) slot)
  1834.         hashheader.upperconv[mslot]++;
  1835.     hashheader.lowerconv[mslot + 1] = hashheader.lowerconv[mslot];
  1836.     hashheader.upperconv[mslot + 1] = hashheader.upperconv[mslot];
  1837.     hashheader.sortorder[mslot + 1] = hashheader.sortorder[mslot];
  1838.     hashheader.stringdups[mslot + 1 - SET_SIZE] =
  1839.       hashheader.stringdups[mslot - SET_SIZE];
  1840.     hashheader.dupnos[mslot + 1 - SET_SIZE] =
  1841.       hashheader.dupnos[mslot - SET_SIZE];
  1842.     }
  1843.     /*
  1844.      * Insert the new string character into the slot we made.  The
  1845.      * caller may choose to change the case-conversion field.
  1846.      */
  1847.     (void) strcpy (&hashheader.stringchars[slot - SET_SIZE][0], (char *) str);
  1848.     hashheader.lowerchars[slot] = (char) lower;
  1849.     hashheader.upperchars[slot] = (char) upper;
  1850.     hashheader.wordchars[slot] = 1;
  1851.     hashheader.boundarychars[slot] = 0;
  1852.     hashheader.sortorder[slot] = hashheader.sortval++;
  1853.     hashheader.lowerconv[slot] = (ichar_t) slot;
  1854.     hashheader.upperconv[slot] = (ichar_t) slot;
  1855.     hashheader.stringdups[slot - SET_SIZE] = slot - SET_SIZE;
  1856.     hashheader.dupnos[slot - SET_SIZE] = 0;
  1857.     /*
  1858.      * Add the first character of the string to the string-starts table, and
  1859.      * bump the count.
  1860.      */
  1861.     hashheader.stringstarts[str[0]] = 1;
  1862.     hashheader.nstrchars++;
  1863.     return slot;
  1864.     }
  1865.  
  1866. /*
  1867.  * This routine is a reimplemention of strcmp(), needed because the
  1868.  * idiots at Sun managed to screw up the implementation of strcmp on
  1869.  * Sun 4's (they used unsigned comparisons, even though characters
  1870.  * default to signed).  I hate hate HATE putting in this routine just
  1871.  * to support the stupidity of one programmer who ought to find a new
  1872.  * career digging ditches, but there are a lot of Sun 4's out there,
  1873.  * so I don't really have a lot of choice.
  1874.  */
  1875. static int stringcharcmp (a,  b)
  1876.     register char *        a;
  1877.     register char *        b;
  1878.     {
  1879.  
  1880. #ifdef NO8BIT
  1881.     while (*a != '\0')
  1882.     {
  1883.     if (((*a++ ^ *b++) & NOPARITY) != 0)
  1884.         return (*--a & NOPARITY) - (*--b & NOPARITY);
  1885.     }
  1886.     return (*a & NOPARITY) - (*b & NOPARITY);
  1887. #else /* NO8BIT */
  1888.     while (*a != '\0')
  1889.     {
  1890.     if (*a++ != *b++)
  1891.         return *--a - *--b;
  1892.     }
  1893.     return *a - *b;
  1894. #endif /* NO8BIT */
  1895.     }
  1896.  
  1897. #ifdef TBLDEBUG
  1898. static void tbldump (flagp, numflags)    /* Dump a flag table */
  1899.     register struct flagent *    flagp;    /* First flag entry to dump */
  1900.     register int        numflags; /* Number of flags to dump */
  1901.     {
  1902.     while (--numflags >= 0)
  1903.     entdump (flagp++);
  1904.     }
  1905.  
  1906. static void entdump (flagp)        /* Dump one flag entry */
  1907.     register struct flagent *    flagp;    /* Flag entry to dump */
  1908.     {
  1909.     register int        cond;    /* Condition number */
  1910.  
  1911.     (void) fprintf (stderr, "flag %s%c:\t",
  1912.       (flagp->flagflags & FF_CROSSPRODUCT) ? "*" : "",
  1913.       BITTOCHAR (flagp->flagbit));
  1914.     for (cond = 0;  cond < flagp->numconds;  cond++)
  1915.     {
  1916.     setdump (flagp->conds, 1 << cond);
  1917.     if (cond < flagp->numconds - 1)
  1918.         (void) putc (' ', stderr);
  1919.     }
  1920.     if (cond == 0)            /* No conditions at all? */
  1921.     (void) putc ('.', stderr);
  1922.     (void) fprintf (stderr, "\t> ");
  1923.     (void) putc ('\t', stderr);
  1924.     if (flagp->stripl)
  1925.     (void) fprintf (stderr, "-%s,", ichartosstr (flagp->strip, 1));
  1926.     (void) fprintf (stderr, "%s\n",
  1927.       flagp->affl ? ichartosstr (flagp->affix, 1) : "-");
  1928.     }
  1929.  
  1930. static void setdump (setp, mask)    /* Dump a set specification */
  1931.     register char *        setp;    /* Set to be dumped */
  1932.     register int        mask;    /* Mask for bit to be dumped */
  1933.     {
  1934.     register int        cnum;    /* Next character's number */
  1935.     register int        firstnz; /* Number of first NZ character */
  1936.     register int        numnz;    /* Number of NZ characters */
  1937.  
  1938.     numnz = 0;
  1939.     for (cnum = SET_SIZE + hashheader.nstrchars;  --cnum >= 0;  )
  1940.     {
  1941.     if (setp[cnum] & mask)
  1942.         {
  1943.         numnz++;
  1944.         firstnz = cnum;
  1945.         }
  1946.     }
  1947.     if (numnz == 1)
  1948.     {
  1949.     if (cnum < SET_SIZE)
  1950.         (void) putc (firstnz, stderr);
  1951.     else
  1952.         (void) fputs (hashheader.stringchars[cnum - SET_SIZE], stderr);
  1953.     }
  1954.     else if (numnz == SET_SIZE)
  1955.     (void) putc ('.', stderr);
  1956.     else if (numnz > SET_SIZE / 2)
  1957.     {
  1958.     (void) fprintf (stderr, "[^");
  1959.     subsetdump (setp, mask, 0);
  1960.     (void) putc (']', stderr);
  1961.     }
  1962.     else
  1963.     {
  1964.     (void) putc ('[', stderr);
  1965.     subsetdump (setp, mask, mask);
  1966.     (void) putc (']', stderr);
  1967.     }
  1968.     }
  1969.  
  1970. static void subsetdump (setp, mask, dumpval) /* Dump part of a set spec */
  1971.     register char *        setp;    /* Set to be dumped */
  1972.     register int        mask;    /* Mask for bit to be dumped */
  1973.     register int        dumpval; /* Value to be printed */
  1974.     {
  1975.     register int        cnum;    /* Next character's number */
  1976.     register int        rangestart; /* Value starting a range */
  1977.  
  1978.     for (cnum = 0;  cnum < SET_SIZE;  setp++, cnum++)
  1979.     {
  1980.     if (((*setp ^ dumpval) & mask) == 0)
  1981.         {
  1982.         for (rangestart = cnum;  cnum < SET_SIZE;  setp++, cnum++)
  1983.         {
  1984.         if ((*setp ^ dumpval) & mask)
  1985.             break;
  1986.         }
  1987.         if (cnum == rangestart + 1)
  1988.         (void) putc (rangestart, stderr);
  1989.         else if (cnum <= rangestart + 3)
  1990.         {
  1991.         while (rangestart < cnum)
  1992.             {
  1993.             (void) putc (rangestart, stderr);
  1994.             rangestart++;
  1995.             }
  1996.         }
  1997.         else
  1998.         (void) fprintf (stderr, "%c-%c", rangestart, cnum - 1);
  1999.         }
  2000.     }
  2001.     for (  ;  cnum < SET_SIZE + hashheader.nstrchars;  setp++, cnum++)
  2002.     {
  2003.     if (((*setp ^ dumpval) & mask) == 0)
  2004.         (void) fputs (hashheader.stringchars[cnum - SET_SIZE], stderr);
  2005.     }
  2006.     }
  2007. #endif
  2008.